﻿//#define SystemReady
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using T = System.Timers;

namespace AuctionHouse
{
    enum hammerslag : byte
    {
        none = 0,
        First = 1,
        Second = 2,
        third = 3
    }


    public delegate void AuctionEvnetHandler();

    class Auction
    {
        #region events

        public event AuctionEvnetHandler AuctionStarted;
        public event AuctionEvnetHandler AuctionEnded;
        public event AuctionEvnetHandler NewLotSet;
        public event AuctionEvnetHandler AllLotsSold;
        public event AuctionEvnetHandler TimeIsUp; // der er ikke længere tid.
        public event AuctionEvnetHandler HammerStroke; // lidt for direkte oversættelse. sker hver gang vi siger enten 1. 2 eller solgt.

        #endregion        

        #region Objects
        
        T.Timer timer = new T.Timer();
        Thread bidWorker; // the worker thread for handling the bids.
        Queue<Bid>bids = new Queue<Bid>(); // the bids that has come in.
        Queue<Lot> LotsToSell = new Queue<Lot>(); // the lots that we are to sell.
        Lot CurrentLot; // the lot currently on auction.
        hammerslag hammer = new hammerslag();

        #endregion

        #region bool flags

        private bool bPendingShutdown = false;
        private bool bHasAuctionEnded;
        private bool bIsSafeToShutDown;
        private bool bHasfirstBidBeenAccepted = false;

        #endregion

        #region Fields
        private double TimePassed;
        #endregion

        #region Constants
        private const double First = 30;
        private const double Second = 40;
        private const double Sold = 50;

        private const double timebetweenAcutins = 5;

        private const double AuctionTimeout = 120.0; // how long before we give up if firstbid has not been recieved.
        #endregion                
        
        #region Properties
        
        public bool ShutdownSafe
        {
            private set { bIsSafeToShutDown = value; }
            get { return bIsSafeToShutDown; }
        }

        #endregion

        public Auction()
        {            
            bidWorker = new Thread(HandleBids);
            hammer = hammerslag.none;
            
            #region Debug Kode
                #if(!SystemReady) // Hvis vi fjerner udkommenteringen fra den aller første linje ville denne kode ikke blive kompileret.

                    PopulateQueuesWithDummyData();

                #endif
            #endregion

            timer.Interval = 1000.0;
            timer.Elapsed += Timer_Elapsed;
            CurrentLot.newHighestBidRecieved +=onNewHighestBidRecieved;
            
            bidWorker.Start();
            
        }

        /// <summary>
        /// Handles the bids that has come from the clients.
        /// </summary>
        private void HandleBids() //kører i baggrunden. Skal køres af én og kun én tråd hele tiden. skal ikke på nogen måde kaldes hverken internt eller eksternt.
        {
            while(!bPendingShutdown)
            {
                if (bHasAuctionEnded)
                {
                    break; // afslutter tråden. 
                }

                if (bids.Count <= 0)
                {
                    continue;
                }
                else
                {

                    Bid WorkingBid = bids.Dequeue();

                    if (WorkingBid.Amount > CurrentLot.HighestBid)
                    {
                        CurrentLot.UpdateHighestBidder(WorkingBid.Amount, WorkingBid.BidderName);
                    }
                }
            }
            
            bIsSafeToShutDown = true;
        }

        /// <summary>
        /// Set the next lot on auction if no more lots are on the list raises AllLotsSold event
        /// </summary>
        private void setNewLot() // skal kaldes når der skal sættes en ny vare til salg.
        {
            if (LotsToSell.Count > 0)
            {
                CurrentLot = LotsToSell.Dequeue();
                setNewLot();
                CurrentLot.newHighestBidRecieved += new HighestBidHandler(onNewHighestBidRecieved);
                StartAuktion();
            }
            else
            {
                AllLotsSold();
            }
        }

        public void onNewHighestBidRecieved()
        {
            bHasfirstBidBeenAccepted = true;
            TimePassed = 0.0;
        }

        public void AcceptNewBid(Bid bid)
        {
            if (!bHasAuctionEnded) // kan vi stadig byde
            {
                bids.Enqueue(bid);// sætter det nye bud i køen.                
            }
            // Bid Rejected
        }

        private void StartAuktion()
        {
            bidWorker = new Thread(HandleBids);
            
            bHasAuctionEnded = false;            
            TimePassed = AuctionTimeout;            
            timer.Start();
            bidWorker.Start();

            CurrentLot.newHighestBidRecieved += new HighestBidHandler(onNewHighestBidRecieved);
            AuctionStarted();
        }

        private void Timer_Elapsed(object sender, T.ElapsedEventArgs e)
        {            
            
            TimePassed += 1.0;

            if (!bHasAuctionEnded)
            {
                if (TimePassed < AuctionTimeout)
                {
                    if (bHasfirstBidBeenAccepted)
                    {
                        if (TimePassed < First) // hvis der er gået mindre tid en der står i konstanten first.
                        {
                            return;
                        }
                        else if (TimePassed == First)
                        {
                            StrokeFirst();
                        }
                        else if (TimePassed == Second)
                        {
                            StrokeSecond();
                        }
                        else if (TimePassed == Sold)
                        {
                            StrokeSold();
                        }
                    }

                    if (TimePassed == AuctionTimeout)
                    {
                        //LotNotSold();
                    }


                }
                else
                {
                    TimeIsUp();
                }
            }

            else
            {
                if (TimePassed == timebetweenAcutins)
                {
                    setNewLot();
                }
            }
        }

        private void EndAuction()
        {
            bHasAuctionEnded = true;
            AuctionEnded();
        }

        #region Hammerslag Metoder
        
        private void StrokeFirst()
        {
            hammer = hammerslag.First;
            HammerStroke();
        }

        private void StrokeSecond()
        {
            hammer = hammerslag.Second;
            HammerStroke();
        }

        private void StrokeSold()
        {
            hammer = hammerslag.third;
            HammerStroke();
            EndAuction();
        }

        #endregion
        
        /// <summary>
        /// Initiates the shutdown procedure.
        /// </summary>
        public void InitiateShutdown()
        {
            bPendingShutdown = true;
        }      

        #region Debug Methods
            #if (!SystemReady)

        private void PopulateQueuesWithDummyData()
        {
            LotsToSell.Enqueue(new Lot()); // testLot
            LotsToSell.Enqueue(new Lot()); // TestLot

            bids.Enqueue(new Bid(100.00m, "birthe"));
            bids.Enqueue(new Bid(101.00m, "Hans"));
            bids.Enqueue(new Bid(110.00m, "birthe"));
            bids.Enqueue(new Bid(105.00m, "Jens"));
        }

            #endif
        #endregion
    }
}
